﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using OMap.Tools;


namespace OMap {

	
	/// <summary>
	/// Implements the logic for mapping property values from one object into another object.
	/// </summary>
    public abstract class BaseMapper : IObjectMapper {
		#region Protected Fields
		protected readonly Type _sourceType;
		protected readonly Type _targetType;
		protected readonly IDynamicAccessor _sourcePropertyAccessor;
		protected readonly IDynamicAccessor _destinationPropertyAccessor;       
		#endregion

		#region Private Fields
		private readonly bool _overwriteTarget;
		protected  OMap _oMap;
		private IObjectMapper _calledBy;
		private Func<IObjectMapper, object> _createTarget;
		protected object _mappedSourceValue;
		protected object _mappedTargetValue;
        
		#endregion

		#region Properties
		/// <summary>
		/// Defines if existing value in the target value should be overwritten if they are none default values.
		/// E.g. if in the target is null the null will be always overwritten, but if there is already a value
		/// it won't be set unless this property was set to true.
		/// </summary>
		/// <remarks>
		/// Please note that if you are using collections and if the collection have already been initialized
		/// (value is not null) and do not have any values inside the collection this value wont be overwritten by the source value if this property was
		/// not set to true.
		/// </remarks>
		public bool OverwriteTarget {
			get {
				return _overwriteTarget;
			}
		}


		/// <summary>
		/// Gets the mapper that was calling the current mapper.
		/// </summary>
		/// <value></value>
		public IObjectMapper CalledBy {
			get {
				return _calledBy;
			}
			set {
				_calledBy = value;
			}
		}

		/// <summary>
		/// Gets the mapper that was calling the current mapper.
		/// </summary>
		/// <value></value>
		public Func<IObjectMapper, object> CreateTarget {
			get {
				return _createTarget;
			}
			set {
				_createTarget = value;
			}
		}

		/// <summary>
		/// Gets the type of the target.
		/// </summary>
		/// <value>The type of the target.</value>
        public Type TargetType{
            get{
                return _targetType;
            }
        }

		/// <summary>
		/// Gets the type of the source.
		/// </summary>
		/// <value>The type of the source.</value>
        public Type SourceType{
            get{
                return _sourceType;
            }
        }

		

		/// <summary>
		/// Gets the mapped source value.
		/// </summary>
		/// <value>The mapped source value.</value>
		public object MappedSourceValue {
			get {
				return _mappedSourceValue;
			}
		}

		/// <summary>
		/// Gets the mapped target value.
		/// </summary>
		/// <value>The mapped target value.</value>
		public object MappedTargetValue {
			get {
				return _mappedTargetValue;
			}
			set
			{
				_mappedTargetValue = value;
			}
		}



		/// <summary>
		/// Gets the O map.
		/// </summary>
		/// <value>The O map.</value>
        public OMap OMap {
            get {
                return _oMap;
            }
        }

		#endregion

		
		/// <summary>
		/// Initializes a new instance of the <see cref="ElementMapper"/> class.
		/// </summary>
		/// <param name="sourceType">Type of the source.</param>
		/// <param name="targetType">Type of the target.</param>
		/// <param name="overwriteIfNotDefault">If set to <c>true</c> values in the target object will be overwritten even if there is already a value in the target object.</param>
		/// <param name="oMap">The oMap Object.</param>
		/// <param name="createTarget">The logic that should be executed to create a new instance.</param>
		public BaseMapper(Type sourceType, Type targetType, bool overwriteIfNotDefault, OMap oMap, Func<IObjectMapper, object> createTarget) {
			_sourceType = sourceType;
			_targetType = targetType;
			_sourcePropertyAccessor = new DynamicAccessor(_sourceType);
			_destinationPropertyAccessor = new DynamicAccessor(_targetType);
			_overwriteTarget = overwriteIfNotDefault;
			_createTarget = createTarget;
			IsInitialized = false;
			this.Initialize(oMap);
		}


		/// <summary>
		/// Maps from the passed source object into a new target object.
		/// </summary>
		/// <param name="source">The source object that should be mapped into a new target object.</param>
		/// <param name="referenceDict">The reference dict.</param>
		/// <returns></returns>
		public abstract object MapTo(object source, Dictionary<object, object> referenceDict);


		/// <summary>
		/// Maps from the passed source object into an existing target object.
		/// </summary>
		/// <param name="source">The source object that should be mapped into a new target object.</param>
		/// <param name="target">The target object that should be used as mapping target.</param>
		/// <param name="referenceDict">The reference dict.</param>
		/// <returns></returns>
		public abstract object MapTo(object source, object target, Dictionary<object, object> referenceDict);

		
		/// <summary>
		/// Initializes the specified Root Mapping Object <see cref="OMap"/>.
		/// </summary>
		/// <param name="oMap">The root mapping object.</param>
		public abstract void Initialize(OMap oMap);

		/// <summary>
		/// Determines whether the current mapper is already initialized or not.
		/// </summary>
		/// <value></value>
		public bool IsInitialized { get; protected set; }
    }
}
